﻿using cn.edu.suda.sumcu.iot;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Sunny.UI;
using System.Windows.Forms.DataVisualization.Charting;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.IO;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using NPOI.SS.Util;

namespace Easily._02_Form._01_frmCtrlConfig
{
    public partial class frmLineChart : Form
    {
        #region 串口变量
        public EMUART emuart;     //串口通信类对象
        //private uint softVersion; //保存终端更新程序版本，0表示更新版本不详，1表示更新版本为VA.10之前，2表示更新版本为VA.10之后
        private bool closing = false;      //标识是否正在关闭串口
        private bool listening = false;    //标识是否执行完invoke相关操作
        private static UInt32 message_count;//
        private string uecomType;   //通信模组类型
        private string mcuType;     //MCU类型
        private uint uStartAds;     //User程序起始地址
        private uint uCodeSize;     //User程序总代码大小
        private uint replaceNum;    //替换更新最大字节
        private uint reserveNum;    //保留更新最大字节（不等于0意味着有User程序）
        //private byte overallStyle;  //整体更新方式
        private string biosVersion; //BIOS版本号 
        //private uint sectorStart;

        //握手帧
        // LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局,字符串转换成ANSI字符串，Pack表示1字节对齐
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct shakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
        }
        [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct newshakeData
        {
            // SizeConst用来定义数组大小
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)]
            public byte[] uecomType;          //通信模组类型
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public byte[] mcuType;            //MCU类型
            public uint uStartAds;            //User程序起始地址
            public uint uCodeSize;            //User程序总代码大小
            public uint replaceNum;           //替换更新最大字节
            public uint reserveNum;           //保留更新最大字节（不等于0意味着有User程序）
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
            public byte[] BIOSVersion;            //BIOS版本号
        }
        private newshakeData newshakedata;
        private shakeData shakedata;
        #endregion
        //[2021/2/1] 添加折线图控件
        private static Dictionary<string, string> Units = new Dictionary<string, string>
        { {"超声波传感器","cm" },{ "声音传感器","db"},{"温度传感器","℃"} };//所有可以读入数据的传感器
        private static List<string> Sensors = new List<string>()
        { "超声波传感器","声音传感器","温度传感器"};
        //[2021/2/24] 存储接收的y值 以动态变化折线图
        //[2021/2/25] 要存储多个传感器的y值
        private Dictionary<string, List<float>> sensorYValueDic = new Dictionary<string, List<float>>();
        private const int ReadBufferSize = 4096;            //接收缓冲区大小设置

        ////private int timeSpan = 500;//用户输入的时间间隔，默认为1s
        private float timeTotal = 0;//记录x轴x值
        private List<string> sensorSelectedVariables = new List<string>();//保存用户选中的显示传感器变量
        //【2021/3/29】所有的数据没有丢 现在针对温度传感器只有6位 进行特殊处理
        private string readBuffer = "";//存储所有接收的字符串
        private Dictionary<string, List<float>> buffer = new Dictionary<string, List<float>>();//存储不同USB口（J1、J2...）发送的串口数据
        //存储折线的颜色
        private readonly List<Color> colors = new List<Color>()
        { 
            Color.Red,Color.Blue,Color.Yellow,Color.Green,Color.Magenta,Color.Black
        };
        private int color_Num;//折线图颜色取哪一个
        //[2021/2/23] 设置折线图样式
        //private UILineOption option;//折线图样式选项
        //private UILineSeries series;//折线图
        //private float[] pointX = new float[9999];
        //private float[] pointY= new float[9999];
        //private int pointCnt = 0;//记录数组下标

        /// <summary>
        /// 构造函数
        ///主要功能：加载保存的控件设置；生成初始代码；
        /// </summary>
        /// <param name="icon"></param>
        public frmLineChart()
        {
            InitializeComponent();
            //获取串口实例
            emuart = EMUART.getInstance();
            //默认显示
            //btn_confirm_Click(new object(), new EventArgs());//触发确认
            color_Num = 0;
        }

        #region 内部函数
        /// <summary>
        /// [2021/2/23]设置折线图样式
        /// </summary>
        private void SetLineChart()
        {
            //定义图表区域
            this.lineChart.ChartAreas.Clear();
            this.lineChart.Series.Clear();
            System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea = new System.Windows.Forms.DataVisualization.Charting.ChartArea(sunny_Sensors.SelectedText);
            //设置图表显示样式
            //chartArea.AxisY.Minimum = 0;
            //chartArea.AxisY.Maximum = 100;
            chartArea.AxisX.Minimum = 0;
            chartArea.AxisX.Interval = float.Parse(txt_Time.Text);
            chartArea.AxisX.MajorGrid.LineColor = Color.Silver;
            chartArea.AxisY.MajorGrid.LineColor = Color.Silver;
            this.lineChart.ChartAreas.Add(chartArea);
            //设置图表x、y最小值、最大值、间隔
            //每个点的x、y值
            foreach (var item in sensorSelectedVariables)
            {
                //定义存储和显示点的容器
                System.Windows.Forms.DataVisualization.Charting.Series series = new System.Windows.Forms.DataVisualization.Charting.Series(item)
                {
                    ChartArea = sunny_Sensors.SelectedText,
                    //设置图表显示样式
                    Color = colors[color_Num++],
                    ChartType = SeriesChartType.Line
                    
                };
                this.lineChart.Series.Add(series);
                //设置标题
                this.lineChart.Titles.Clear();
                this.lineChart.Titles.Add(sunny_Sensors.SelectedText);
                this.lineChart.Titles[0].Text = sunny_Sensors.SelectedText;
                this.lineChart.Titles[0].ForeColor = Color.RoyalBlue;
                this.lineChart.Titles[0].Font = new System.Drawing.Font("Microsoft Sans Serif", 12F);
            }
        }

        /// <summary>
        /// 添加变量至variables列表
        /// </summary>
        /// <param name="var"></param>
        private void AddVar(string var)
        {
            if (!sensorSelectedVariables.Contains(var))
                sensorSelectedVariables.Add(var);
        }

        /// <summary>
        /// 从variables列表去除变量
        /// </summary>
        /// <param name="var"></param>
        private void RemoveVar(string var)
        {
            if (sensorSelectedVariables.Contains(var))
                sensorSelectedVariables.Remove(var);
        }

        //======================================================================
        //函数名称：txtShow_rece_data
        //函数返回：无
        //参数说明：str:从下位机获取传感器信息内容
        //功能概要：整体更新过程中，在终端执行信息提示框txt_uartinfo1中追加提示
        //          信息，并显示最新信息      
        //======================================================================
        private void txtShow_rece_data(byte[] data)
        {
            byte[] showData = data;   //临时数组         

            //将字符串转为汉字
            string str = Encoding.GetEncoding("GBK").GetString(showData);

            readBuffer += str;//读取字符串放入缓冲区

            //Console.WriteLine("获取数据返回：" + readBuffer);
            if (readBuffer.Length >= 10)
            {
                if (readBuffer.StartsWith("J") && Regex.IsMatch(readBuffer.Substring(2, 2), "[1-9]"))
                {
                    string usb = readBuffer.Substring(0, 2);//取出USB口名
                    //Console.WriteLine("正确返回：" + usb);
                    int index = 0;
                    for (int i = 2; i < readBuffer.Length; i++)
                    {
                        //字符串的头已知已经以J+数字开头 现在就是找到尾巴也是以J+数字结尾
                        //J是两位
                        if (readBuffer[i] == 'J')
                        {
                            index = i - 2;//J前面以为就是要取的数字尾
                            break;
                        }
                    }
                    //如果buffer中没有存储了该usb口的数据 则新建一个列表
                    if (!buffer.ContainsKey(usb))
                        buffer[usb] = new List<float>();
                    //确保存在USB口存储数据的列表 添加数据
                    //Console.WriteLine("正确返回：" + readBuffer.Substring(2, index));
                    buffer[usb].Add(float.Parse(readBuffer.Substring(2, index)));
                    //取出正确数据后 删除该字符串头 继续向下处理
                    //Console.WriteLine("正确返回：" + readBuffer.Substring(0, index + 4));
                    readBuffer = readBuffer.Remove(0, index + 4);
                }
            }
            

        }
        //======================================================================
        //函数名称：DataRecv
        //函数返回：无
        //参数说明：data:待显示数据的二进制形式
        //功能概要：执行异步操作，将接收到的调试数据显示到文本控件上
        //======================================================================
        private void DataRecv(byte[] data)
        {
            //如果正在执行关闭操作
            if (closing)
            {
                return;
            }
            try
            {

                byte[] showData = data;

                message_count++;
                listening = true;    //开始处理数据，可能会用到多线程
                /*[20200505]
                if (this.txt_updateinfo1.InvokeRequired)
                {
                    this.txt_updateinfo1.Invoke(new Action(() => { txtShow_rece_data(showData); }));
                }
                else
                */
                txtShow_rece_data(showData);
                emuart.bufferClear();
                System.Windows.Forms.Application.DoEvents();
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                listening = false;
            }
        }

        //======================================================================
        //函数名称：bytesToStruct
        //函数返回：byte数组转换为对应的结构体
        //参数说明：bytes:字节数组;type:结构体类型
        //功能概要：将byte字节数组数据转换为对应的结构体数据
        //======================================================================
        private object bytesToStruct(byte[] bytes, Type type)
        {
            //（1）变量声明
            int size;
            object obj;
            IntPtr structPtr;

            size = Marshal.SizeOf(type);
            //（2）判断字节长度
            if (size > bytes.Length) return null;
            //（3）分配结构体内存空间
            structPtr = Marshal.AllocHGlobal(size);
            //（4）将byte数组拷贝到分配好的内存空间
            Marshal.Copy(bytes, 0, structPtr, size);
            //（5）将内存空间转换为目标结构体
            obj = Marshal.PtrToStructure(structPtr, type);
            //（6）释放内存空间
            Marshal.FreeHGlobal(structPtr);

            return obj;
        }

        #endregion

        #region 事件
        /// <summary>
        /// 根据用户设定和串口接收到的数据生成chart图表
        /// 并且把用户设定值保存到code中
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_confirm_Click(object sender, EventArgs e)
        {
            if (lab_x.Text == "")
                tip_x.Text = "警告：请填写时间间隔（x轴）！";
            else
            {
                //设置计时器间隔 将s转换成ms
                timer.Interval = (int)(float.Parse(txt_Time.Text) * 1000);
                //timeSpan = timer.Interval;//设置时间间隔
                //开始计时 
                timer.Start();                
                //跳转到查看折线图
                tabControl1.SelectedIndex = 1;
                SetLineChart();

            }
        }

        /// <summary>
        /// 根据所有变量生成复选框
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmLineChart_Load(object sender, EventArgs e)
        {
            //1.获取所有能读入值的传感器，加载到下拉框中
            foreach (var item in Globaldefine.loadConfig.control_Cnt.Keys)
            {
                if (Sensors.Contains(item) && Globaldefine.loadConfig.control_Cnt[item] > 0)
                {
                    sunny_Sensors.Items.Add(item);
                }
            }
        }


        /// <summary>
        /// 只允许写入数字
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txt_Time_KeyPress(object sender, KeyPressEventArgs e)
        {
            //8是允许输入退格
            if (e.KeyChar != 8 && !Char.IsDigit(e.KeyChar) && e.KeyChar != '.')
            {
                e.Handled = true;
            }
        }

        #endregion


        /// <summary>
        /// 计时器根据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer_Tick(object sender, EventArgs e)
        {
            //【20210327】 timer.Interval以毫秒计数 需要除以1000
            timeTotal += timer.Interval * 1.0f / 1000;
            //[2021/2/9] 时间间隔一到 就读取串口传输过来的数据
            //记录当前y轴最大值和最小值
            List<float> data = new List<float>();//临时存储传感器数据 方便使用和理解
            float sensorYValuemax = float.MinValue, sensorYValuemin = float.MaxValue;
            foreach (var sensor in sensorSelectedVariables)
            {
                //如果当前折线图中没有该传感器的y值 那么新建一个列表存储
                if (!sensorYValueDic.ContainsKey(sensor))
                {
                    sensorYValueDic.Add(sensor, new List<float>());
                }
                //[2021/2/26] 串口接收不同传感器的数据 
                //[2021/3/18] 根据USB口 取出该传感器对应数据
                if (buffer.ContainsKey(Globaldefine.loadConfig.dicHard_USB[sensor]))
                {
                    data = buffer[Globaldefine.loadConfig.dicHard_USB[sensor]];//取出数据
                    if (data.Count > 0)
                        sensorYValueDic[sensor].Add(data[data.Count - 1]);//每次只取buffer中的最新数据
                }

                //不断的更新图表的最大值和最小值范围，使得折线图总是显示最好看。
                if (sensorYValueDic[sensor].Count > 0)
                {
                    if (sensorYValueDic[sensor].Max() > sensorYValuemax)
                    {
                        sensorYValuemax = sensorYValueDic[sensor].Max();
                    }
                    if (sensorYValueDic[sensor].Min() < sensorYValuemin)
                    {
                        sensorYValuemin = sensorYValueDic[sensor].Min();
                    }
                    //仅修改Y轴的值
                    lineChart.Series[sensor].Points.AddXY(timeTotal, data[data.Count - 1]);
                    //serie.Points.AddY();
                    lineChart.Series[sensor].Points.ResumeUpdates();
                }
            }
            //所有折线共用一个区域 所以只要设置一次
            lineChart.ChartAreas[0].AxisY.Maximum = sensorYValuemax + 10;
            lineChart.ChartAreas[0].AxisY.Minimum = sensorYValuemin - 10;
            lineChart.ChartAreas[0].AxisX.Maximum = timeTotal;
            lineChart.ChartAreas[0].AxisX.Minimum = 0;
            //buffer.Clear();
            //Thread.Sleep(10);

        }


        //======================================================================
        //函数名称：btn_uartcheck_Click
        //函数返回：无
        //参数说明：无
        //功能概要：“连接串口”按钮点击事件，重新连接终端
        //======================================================================
        private void btn_uartcheck_Click(object sender, EventArgs e)
        {
            //【1】变量声明
            int ret;            //返回值
            string com = "";    //串口信息
            string sTemp = "";
            byte[] recv = null;//保存串口接收信息
            byte[] shake = { (byte)10, (byte)'s', (byte)'h', (byte)'a', (byte)'k', (byte)'e' }; //与终端握手帧数据


            //【2】清除一些可能余留信息
            //this.lbl_uartstate.Text = "";       //右上角显示区
            tip.Text = "运行状态：正在连接串口...";     //底部提示
            this.Refresh();                     //刷新显示 
            btn_uartcheck.Text = "重新连接串口"; //更改显示文字            

            //【3】重新遍历串口，寻找终端
            if ( emuart._Uartport != null)
            {
                //【20191024】  (3.1)让耗时操作做完，如果不做完耗时操作，无法进行下面的事件，且界面不响应
                System.Windows.Forms.Application.DoEvents();
                emuart._Uartport.Close();
                emuart._Uartport.Dispose();
                //【20200401】 姜家乐 增加延时函数
                Thread.Sleep(10);
            }
            //Thread.Sleep(50);   //等待串口关闭
            emuart.RawDataReceivedEvent -= new EMUART.RawDataReceived(DataRecv);//解除事件绑定的方法，加快握手速度
            if (emuart != null) emuart = null;

            emuart = EMUART.getInstance();    //每次“重新连接”重新实例化
            tip.Text = "运行状态：正在连接串口...正在查找匹配设备...";     //底部提示
            ret = emuart.findDevice(out com, 115200);  //寻找emuart设备
            //this.lbl_uartstate.Text = com;       //右上角显示区
            tip.Text = "运行状态：正在连接串口...尝试与匹配设备建立通信...";     //底部提示

            //【4】根据寻找串口的返回值确定执行下列分支
            //【4.1】如果连接终端的emuart失败，退出函数
            if (ret == 1) goto btn_uartcheck_Click_EXIT1;

            //【4.2】找到串口，没有找到UE，退出函数
            else if (ret == 2) goto btn_uartcheck_Click_EXIT2;

            //【4.3】如果找到串口与UE,向设备发送握手帧
            tip.Text = "运行状态：正在连接串口...向该设备发送握手信息，准备接收设备返回消息...";     //底部提示
            emuart.bufferClear();   //清除接收数组缓冲区
            emuart.send(shake, out recv, 100, 3); //获得设备的信息在recv中
            //this.btn_SaveExcel.Enabled = true;     //允许选择文件功能
            tip.Text = "运行状态：正在连接串口...正在处理设备返回信息...";     //底部提示
            if (recv == null) goto btn_uartcheck_Click_EXIT3;//未收到数据，退出   


            //【4.4】发送握手帧后，若收到设备返回数据，处理之
            //【4.4.1】如果终端的更新程序版本为VA.10以后的版本（通用于所有GEC芯片）
            int length1 = Marshal.SizeOf(typeof(shakeData));
            int length = Marshal.SizeOf(typeof(newshakeData));

            if (recv.Length == Marshal.SizeOf(typeof(shakeData)))
            {
                tip.Text = "运行状态：成功连接到GEC设备";     //底部提示
                //softVersion = 2;  //表示终端更新程序为A.10之后的通用版本
                //byte数组转结构体
                try
                {
                    shakedata = (shakeData)bytesToStruct(recv, typeof(shakeData));
                    //获取握手帧数据
                    uecomType = Encoding.Default.GetString(shakedata.uecomType).Replace("\0", "");
                    mcuType = Encoding.Default.GetString(shakedata.mcuType).Replace("\0", "");
                    uStartAds = shakedata.uStartAds;    //User程序起始地址
                    uCodeSize = shakedata.uCodeSize;
                    replaceNum = shakedata.replaceNum;
                    reserveNum = shakedata.reserveNum;
                    //sectorStart = uStartAds / uCodeSize;    //BIOS 中读取的用户程序扇区号
                }
                catch (Exception)
                {
                    throw;
                }
                //设置设备信息                   
                sTemp = com + "：" + uecomType + " " + mcuType;  //设备信息
                //状态提示
                //this.lbl_uartstate.Text = sTemp;     //右上角显示区
                tip.Text = "运行状态：" + sTemp;   //底部提示
                //fmain.lbl_protocal.Text = "协议：串口";    //底部中间协议类型
                //fmain.lbl_location.Text = "协议信息：端口" + com + ",波特率115200";   //底部右侧协议信息
                //this.txt_updateinfo1.Text = sTemp + "\r\n";     //右侧更新提示区     
                                                                //若保留更新最大字节为0表示目前没有User程序，此时只能进行整体更新

                //if (reserveNum == 0)
                //{
                //    txtShow1("可进行GEC中User程序更新\r\n");     //右侧更新提示区  
                //}
                //如果存在串口，则允许接收调试信息
                if (emuart.haveUE)
                {
                    //绑定串口结束事件，处理串口调试信息
                    emuart.RawDataReceivedEvent += new EMUART.RawDataReceived(DataRecv);
                    tip.Text = "过程提示:串口接收界面开启成功";   //底部提示
                }
            }

            //【4.4.2】如果终端的更新程序版本为VA.10及其以前的版本
            else if (System.Text.Encoding.Default.GetString(recv).Contains("shake:GEC-"))
            {
                //softVersion = 1;   //表示终端更新程序为A.10及之前的版本
                sTemp = "MKL36Z64" + "(" + com + ")";//设备信息
                //this.lbl_uartstate.Text = "成功连接" + sTemp;       //右上角显示区
                tip.Text = "运行状态：正在连接串口...成功连接" + sTemp;   //底部提示
                //this.lbl_uartstate.Text = "找到终端设备" + sTemp;   //右上角显示区
                //fmain.lbl_protocal.Text = "协议：串口";    //底部中间协议类型
                //fmain.lbl_location.Text = "协议信息：端口" + com + ",波特率115200";   //底部右侧协议信息
                //如果存在串口，则允许接收调试信息
                if (emuart.haveUE)
                {
                    //绑定串口结束事件，处理串口调试信息
                    emuart.RawDataReceivedEvent += new EMUART.RawDataReceived(DataRecv);
                    tip.Text = "过程提示:串口接收界面开启成功";   //底部提示
                }
            }
            else if (recv.Length == Marshal.SizeOf(typeof(newshakeData)))
            {
                tip.Text = "运行状态：成功连接到串口";     //底部提示
                //softVersion = 2;  //表示终端更新程序为A.10之后的通用版本
                //byte数组转结构体
                try
                {
                    newshakedata = (newshakeData)bytesToStruct(recv, typeof(newshakeData));
                    //获取握手帧数据
                    uecomType = Encoding.Default.GetString(newshakedata.uecomType).Replace("\0", "");
                    mcuType = Encoding.Default.GetString(newshakedata.mcuType);
                    uStartAds = newshakedata.uStartAds;    //User程序起始地址
                    uCodeSize = newshakedata.uCodeSize;
                    replaceNum = newshakedata.replaceNum;
                    reserveNum = newshakedata.reserveNum;
                    //【20200715 1/2】 获取结构体中的BIOS版本信息
                    biosVersion = Encoding.Default.GetString(newshakedata.BIOSVersion);
                    //sectorStart = uStartAds / uCodeSize;    //BIOS 中读取的用户程序扇区号
                }
                catch (Exception)
                {
                    throw;
                }
                //设置设备信息  
                //【20200715 2/2】 将版本信息显示出来 
                sTemp = com + "：" + " " + mcuType;  //设备信息
                //#region 记录设备信息，重新打开串口
                //PublicVar.g_SCIComNum = com;
                //#endregion
                //状态提示
                //this.lbl_uartstate.Text = sTemp;     //右上角显示区
                //this.lbl_uartstate.Text += "\r\n" + biosVersion;
                tip.Text = "运行状态：" + sTemp;   //底部提示
                //fmain.lbl_protocal.Text = "协议：串口";    //底部中间协议类型
                //fmain.lbl_location.Text = "协议信息：端口" + com + ",波特率115200";   //底部右侧协议信息
                //this.txt_updateinfo1.Text = sTemp + "\r\n";     //右侧更新提示区     
                //若保留更新最大字节为0表示目前没有User程序，此时只能进行整体更新
                //if (reserveNum == 0)
                //{
                //    txtShow1("可进行GEC中User程序更新\r\n");     //右侧更新提示区  
                //}
                //如果存在串口，则允许接收调试信息
                if (emuart.haveUE)
                {
                    //绑定串口结束事件，处理串口调试信息
                    emuart.RawDataReceivedEvent += new EMUART.RawDataReceived(DataRecv);
                    tip.Text = "过程提示:串口接收界面开启成功";   //底部提示
                    btn_uartcheck.Symbol = 61475;//推测：应该是把开锁图标改成加锁
                }
            }
            //【4.4.3】若收到错误返回信息，退出函数
            else goto btn_uartcheck_Click_EXIT3;

            //连接成功 可以点击生成折线图
            btnCreatLineChart.Enabled = true;

            //emuart.bufferClear();
            //if (emuart._Uartport.IsOpen)
            //{
            //    Console.WriteLine("串口已打开");
            //}
            //int len = emuart._Uartport.BytesToRead;
            //Console.WriteLine("接收字节：{0}", len);
            //byte[] data = new byte[len];
            //emuart._Uartport.Read(data, 0, len);
            //if (data.Length > 0)
            //{
            //    Console.WriteLine(data[0]);
            //}
            //else
            //{
            //    Console.WriteLine(emuart._Uartport.PortName);
            //    Console.WriteLine("nothing");
            //}
            //接收数据;
            //emuart.RawDataReceivedEvent += new EMUART.RawDataReceived(DataRecv);


            //emuart.RawDataReceivedEvent -= new EMUART.RawDataReceived(DataRecv);

        //【5】退出区
        // 【5.1】退出函数
        btn_uartcheck_Click_EXIT:
            return;

        //【5.2】不存在可用串口
        btn_uartcheck_Click_EXIT1:
            //this.btn_autoupdate1.Enabled = false;    //禁止串口更新操作
            //this.btn_SaveExcel.Enabled = false;      //禁止选择文件功能
            //this.lbl_uartstate.Text = "当前不存在可用串口";       //右上角显示区
            tip.Text = "运行状态：当前不存在可用串口";   //底部提示
            //fmain.lbl_protocal.Text = "协议：";    //底部中间协议类型
            //fmain.lbl_location.Text = "协议信息："; //底部右侧协议信息
            //this.txt_updateinfo1.Text = "没有找到USB串口，可能原因如下：\r\n（1）USB串口未插上PC\r\n（2）PC未安装串口驱动\r\n";     //右侧更新提示区
            closing = true;     //正在关闭串口
            while (listening)
            {
                System.Windows.Forms.Application.DoEvents();
            }
            if (emuart._Uartport != null)
            {
                emuart._Uartport.Close();
            }
            closing = false;    //关闭完成
            goto btn_uartcheck_Click_EXIT;

        //【5.3】存在串口，但不存在emuar设备
        btn_uartcheck_Click_EXIT2:
            emuart.terminate(115200); //发送数据给终端设备，让终端设备清空其数据缓冲区
            //this.lbl_uartstate.Text = "已连接串口" + com + "但未找到设备";       //右上角显示区
            tip.Text = "运行状态：已连接串口" + com + "但未找到设备";   //底部提示
            //fmain.lbl_protocal.Text = "协议：";   //底部中间协议类型
            //fmain.lbl_location.Text = "协议信息：";    //底部右侧协议信息
            tip.Text = "运行状态：与设备建立连接失败，请尝试重新连接设备";   //底部提示
            //右侧更新提示区
            //this.txt_updateinfo1.Text = "有USB串口，但未连上终端，可能原因如下：\r\n（1）USB串口驱动需更新\r\n（2）USB串口未连接终端\r\n（3）终端程序未运行\r\n";
            closing = true;     //正在关闭串口
            while (listening)
            {
                System.Windows.Forms.Application.DoEvents();
            }
            emuart._Uartport.Close();
            emuart._Uartport.Dispose();
            Thread.Sleep(10);
            closing = false;    //关闭完成
            goto btn_uartcheck_Click_EXIT;

        //【5.4】未收到返回信息或收到错误返回信息
        btn_uartcheck_Click_EXIT3:
            emuart.terminate(115200); //发送数据给终端设备，让终端设备清空其数据缓冲区
            tip.Text = "运行状态：正在连接GEC...未收到返回信息";     //底部提示
            //this.lbl_uartstate.Text = "找到GEC在" + com + "但握手失败，请再次单击[重新连接]按钮";  //右上角显示
            closing = true;     //正在关闭串口
            while (listening)
            {
                System.Windows.Forms.Application.DoEvents();
            }
            //【20191205 1/2】  程宏玉   解决无串口连接时单击连接GEC按钮，出现异常问题
            if (emuart._Uartport != null)
            {
                try
                {
                    System.Windows.Forms.Application.DoEvents();
                    emuart._Uartport.Close();
                    emuart._Uartport.Dispose();
                    Thread.Sleep(10);
                }
                catch (Exception)
                {

                }
            }
            closing = false;    //关闭完成
            goto btn_uartcheck_Click_EXIT;

        }



        
        //======================================================================
        //函数名称：infor_dispaly_button_Click
        //函数返回：无
        //参数说明：无
        //功能概要：暂停或启动右侧提示区的数据显示
        //======================================================================
        private void infor_dispaly_button_Click(object sender, EventArgs e)
        {
            if (infor_dispaly_button.Text == "暂停传输")
            {
                infor_dispaly_button.Text = "继续传输";
                infor_dispaly_button.Symbol = 61515;
                emuart.RawDataReceivedEvent -= new EMUART.RawDataReceived(DataRecv);//解除事件绑定的方法,暂停数据接收
                btn_SaveExcel.Enabled = true;
                btnSavePicture.Enabled = true;
                timer.Stop();

            }

            else
            {
                infor_dispaly_button.Text = "暂停传输";
                infor_dispaly_button.Symbol = 61517;
                emuart.RawDataReceivedEvent += new EMUART.RawDataReceived(DataRecv);//解除事件绑定的方法,开始数据接收
                btn_SaveExcel.Enabled = false;
                btnSavePicture.Enabled = false;
                timer.Start();
            }

        }

        private void sunny_Sensors_SelectedValueChanged(object sender, EventArgs e)
        {
            //2.根据选择的传感器
            //根据全局变量和局部变量生成复选框
            //存储所有复选框至网格布局
            TableLayoutPanel tableLayout = new TableLayoutPanel()
            {
                AutoSize = true
            };
            for (int i = 0; i < Globaldefine.loadConfig.control_Cnt[((UIComboBox)sender).SelectedItem.ToString()]; i++)
            {
                UICheckBox checkBox = new UICheckBox()
                {
                    //Text = ((UIComboBox)sender).SelectedItem.ToString()+(i+1),
                    Font = new System.Drawing.Font("宋体", 12)
                };
                string sensorName = ((UIComboBox)sender).SelectedItem.ToString();//获取传感器名
                if (i==0)
                {
                    checkBox.Text = sensorName;
                }
                else
                {
                    checkBox.Text = sensorName + i;
                }
                checkBox.ValueChanged += CheckBox_ValueChanged;
                    //+= CheckBox_CheckedChanged;
                //checkBox.CheckedChanged += CheckBox_CheckedChanged;
                tableLayout.Controls.Add(checkBox);
            }
            //把网格布局加入到分割布局的panel2中
            splitContainer_y.Panel2.Controls.Add(tableLayout);
            //3.修改变量间隔的单位
            if (((UIComboBox)sender).SelectedItem == null || ((UIComboBox)sender).SelectedItem.ToString() == "请选择传感器")
            {
                tip_y.Text = "请选择传感器！";
            }
            //else
            //{
            //    lab_Unit.Text = Units[sunny_Sensors.SelectedItem.ToString()];
            //}
        }

        private void CheckBox_ValueChanged(object sender, bool value)
        {
            UICheckBox checkBox = (UICheckBox)sender;
            if (checkBox.Checked)
                AddVar(checkBox.Text);
            else
                RemoveVar(checkBox.Text);
        }

        private void btn_Save_Click(object sender, EventArgs e)
        {     
            //下面是使用Microsoft.Office.Interop.Excel
            //将采集到的传感器数据以表格形式 存储到excel中
            #region Microsoft.Office.Interop.Excel导出Excel
            //Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
            //if (xlApp == null)
            //{
            //    MessageBox.Show("无法创建Excel对象，可能您的机子未安装Excel");
            //    return;
            //}
            //try
            //{
            //    Workbooks wbs = xlApp.Workbooks;//创建一个工作簿
            //    Workbook wb = wbs.Add(XlWBATemplate.xlWBATWorksheet);//指定要创建的工作簿的类型:工作表
            //    Worksheet ws = (Worksheet)wb.Worksheets[1];//指定工作表
            //    //Excel中列数 行数都从1开始计数 设定表格标题
            //    Range range = xlApp.Range[ws.Cells[1, 1], ws.Cells[1, sensorSelectedVariables.Count + 1]];//指定某一区域
            //    range.Font.Size = 10;
            //    range.Font.Bold = true;
            //    range.Interior.ColorIndex = 15;
            //    range.HorizontalAlignment = Constants.xlCenter;
            //    int colindex = 0, rowindex = 0;//当前列数 行数 
            //    object[,] objx = new object[sensorYValueDic.Values.First().Count + 1, colindex + 1];//这里是obj数组，索引从0开始
            //    objx[rowindex, colindex] = "时间(x轴)单位：" + lab_s.Text;

            //    range = ws.Range[xlApp.Cells[1, 1], xlApp.Cells[sensorYValueDic.Values.First().Count + 1, colindex + 1]];
            //    for (rowindex = 1; rowindex < objx.Length; rowindex++)
            //    {
            //        objx[rowindex, colindex] = "'" + rowindex * float.Parse(txt_Time.Text);
            //    }
            //    range.Value2 = objx;
            //    foreach (var sensor in sensorSelectedVariables)
            //    {
            //        object[,] objy = new object[sensorYValueDic[sensor].Count + 1, 1];//这里是obj数组，索引从0开始           
            //        objy[0, 0] = sensor + "值(y轴)单位：" + Units[sunny_Sensors.SelectedItem.ToString()];
            //        for (rowindex = 1; rowindex < sensorYValueDic[sensor].Count + 1; rowindex++)
            //        {
            //            objy[rowindex, 0] = "'" + sensorYValueDic[sensor][rowindex - 1];
            //            System.Windows.Forms.Application.DoEvents();
            //        }
            //        //x轴占了第一列 所以colindex要加1
            //        range = ws.Range[xlApp.Cells[1, ++colindex + 1], xlApp.Cells[sensorYValueDic[sensor].Count + 1, colindex + 1]];
            //        range.Value2 = objy;
            //    }
            //    wb.Saved = true;
            //    //在当前工作表中根据数据生成图表
            //    //CreateChart(wb, ws, sensorSelectedVariables.Count+2);
            //    wb.SaveAs(Path.Combine(Globaldefine.exePath, "折线图生成数据", sunny_Sensors.SelectedText + ".xls"));
            //    //wb.SaveCopyAs();
            //}
            //catch (Exception ex)
            //{
            //    MessageBox.Show("导出文件时出错,文件可能正被打开！\n" + ex.Message);
            //}
            //finally
            //{
            //    MessageBox.Show("导出文件成功！已保存在" +
            //        Path.Combine(Globaldefine.exePath, "折线图生成数据", sunny_Sensors.SelectedText + ".xls"));
            //    xlApp.Quit();
            //    GC.Collect();
            //}

            #endregion

            #region 使用NPOI导出Excel
            //（1）创建工作薄
            IWorkbook wb = new XSSFWorkbook();
            //（2）创建工作表
            ISheet sheet = wb.CreateSheet();
            int colindex = 1, rowindex = 0;//当前列数(时间作为第一列) 行数
            //（3）创建时间作为第一列 并用时间间隔填充单元格数据
            IRow row_time = sheet.CreateRow(0);
            row_time.CreateCell(0).SetCellValue("时间(x轴)单位：" + lab_s.Text);
            for (rowindex = 1; rowindex < sensorYValueDic.Values.First().Count + 1; rowindex++)
            {
                ICell cell = sheet.CreateRow(rowindex).CreateCell(0);
                cell.SetCellValue(rowindex * float.Parse(txt_Time.Text));
            }
            //（4）循环填充数据

            foreach (var sensor in sensorSelectedVariables)
            {
                row_time.CreateCell(colindex).SetCellValue(sensor + "值(y轴)单位：" + Units[sunny_Sensors.SelectedItem.ToString()]);
                for (rowindex = 1; rowindex < sensorYValueDic[sensor].Count + 1; rowindex++)
                {
                    IRow row;
                    ICell cell;
                    if (sheet.GetRow(rowindex) == null)
                        row = sheet.CreateRow(rowindex);
                    else
                        row = sheet.GetRow(rowindex);
                    if (row.GetCell(colindex) == null)
                        cell = row.CreateCell(colindex);
                    else
                        cell = row.GetCell(colindex);
                    cell.SetCellValue(sensorYValueDic[sensor][rowindex - 1]);
                }
                colindex++;
            }
            // 保存文件
            string fileName = sunny_Sensors.SelectedText;
            string excelName = Path.Combine(Globaldefine.exePath, "折线图生成数据", fileName + ".xls");
            int subfix = 1;
            while(File.Exists(excelName))
            {
                fileName = sunny_Sensors.SelectedText + subfix;
                excelName = Path.Combine(Globaldefine.exePath, "折线图生成数据", fileName + ".xls");
                subfix++;
            }
            FileStream sw = File.Create(excelName);
            wb.Write(sw);
            sw.Close();
            MessageBox.Show("导出文件成功！已保存在" + excelName);
            #endregion

        }

        /// <summary>
        /// 退出折线图功能，关闭串口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCloseUpdate_Click(object sender, EventArgs e)
        {
            if (emuart._Uartport != null)
            {
                emuart.RawDataReceivedEvent -= new EMUART.RawDataReceived(DataRecv);
                closing = true;
                while (listening)
                {
                    System.Windows.Forms.Application.DoEvents();
                }
                emuart._Uartport.Close();
            }
            this.Close();
            Thread.Sleep(10);
            closing = false;
        }

        private void frmLineChart_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (emuart._Uartport != null)
            {
                emuart.RawDataReceivedEvent -= new EMUART.RawDataReceived(DataRecv);
                closing = true;
                while (listening)
                {
                    System.Windows.Forms.Application.DoEvents();
                }
                emuart._Uartport.Close();
            }
            Thread.Sleep(10);
            closing = false;
        }

        private void btnSavePicture_Click(object sender, EventArgs e)
        {
            // 保存文件
            string fileName = lineChart.Series[0].Name;
            string pictureName = Path.Combine(Globaldefine.exePath, "折线图生成数据", fileName + ".png");
            int subfix = 1;
            while (File.Exists(pictureName))
            {
                fileName = sunny_Sensors.SelectedText + subfix;
                pictureName = Path.Combine(Globaldefine.exePath, "折线图生成数据", fileName + ".png");
                subfix++;
            }
            lineChart.SaveImage(pictureName, ChartImageFormat.Png);
            MessageBox.Show("折线图已导出到" + pictureName);
        }

        /// <summary>
        /// 打开“折线图生成数据”文件夹
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_Open_Click(object sender, EventArgs e)
        {
            string v_OpenFolderPath = Path.Combine(Globaldefine.exePath, "折线图生成数据"); 
            System.Diagnostics.Process.Start("explorer.exe", v_OpenFolderPath);
        }
    }
}


////【20190506】-3/3 ---解决汉字显示乱码问题
////如果上一次发送出现汉字断码
//if (lastCH != 0)
//{
//    showData = new byte[data.Length + 1];   //临时数组增1一个字节
//    showData[0] = lastCH;                   //上次保留的字节填入前端
//    Array.Copy(data, 0, showData, 1, data.Length);  //其他数据填充后部
//    lastCH = 0;                                     //lastCH清0
//    data = showData;                                //指针等同                    
//}
////从后向前统计，
//while ((count < data.Length) && (data[data.Length - 1 - count] > 128))
//{
//    count++;
//}
////如果连续大于128的字节数为奇数，则将最后一位保存，与下一次的数据一起组合
//if (count % 2 == 1)
//{
//    showData = new byte[data.Length - 1];
//    Array.Copy(data, 0, showData, 0, data.Length - 1);
//    lastCH = data[data.Length - 1];
//}